import tushare as ts
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 初始化pro接口
pro = ts.pro_api('276da5751ba9fda134012d591c37d0cbed93bdf1dad172617e265d6a')


def fetch_stock_data(ts_codes, start_date, end_date):
    all_data = []
    for ts_code in ts_codes:
        df = pro.daily(
            ts_code=ts_code,
            start_date=start_date,
            end_date=end_date,
            fields=[
                "ts_code",
                "trade_date",
                "open",
                "high",
                "low",
                "close",
                "pre_close",
                "change",
                "pct_chg",
                "vol",
                "amount"
            ]
        )
        df = df.sort_values(by='trade_date')
        df['trade_date'] = pd.to_datetime(df['trade_date'])
        all_data.append(df)
    return all_data


def generate_trading_signals(data):
    data['signal'] = 0
    # 简单示例：金叉信号（短期均线穿过长期均线）
    short_window = 5
    long_window = 20
    data['short_mavg'] = data['close'].rolling(window=short_window, min_periods=1, center=False).mean()
    data['long_mavg'] = data['close'].rolling(window=long_window, min_periods=1, center=False).mean()
    data['signal'] = np.where(data['short_mavg'] > data['long_mavg'], 1, 0)
    data['position'] = data['signal'].diff()
    return data


def select_and_sort_stocks(all_data):
    # 简单示例：按平均成交量排序选股
    volume_means = []
    for data in all_data:
        volume_means.append(data['vol'].mean())
    sorted_indices = np.argsort(volume_means)[::-1]
    sorted_data = [all_data[i] for i in sorted_indices]
    return sorted_data


def backtest(data, stop_loss=0.1, stop_profit=0.2):
    initial_capital = float(100000.0)
    positions = pd.DataFrame(index=data.index).fillna(0.0)
    cash = initial_capital
    holdings = 0
    portfolio_values = []

    if not data.empty:
        positions['stock'] = data['signal'] * initial_capital / data['close'][0]

    for i in range(len(data)):
        if data['position'].iloc[i] == 1:  # 买入信号
            shares = cash // data['close'].iloc[i]
            positions['stock'].iloc[i] = shares
            cash -= shares * data['close'].iloc[i]
            cost_basis = data['close'].iloc[i]
        elif data['position'].iloc[i] == -1:  # 卖出信号
            cash += positions['stock'].iloc[i - 1] * data['close'].iloc[i]
            positions['stock'].iloc[i] = 0
        # 止损
        if positions['stock'].iloc[i] > 0:
            current_value = positions['stock'].iloc[i] * data['close'].iloc[i]
            if (data['close'].iloc[i] / cost_basis - 1) < -stop_loss:
                cash += positions['stock'].iloc[i] * data['close'].iloc[i]
                positions['stock'].iloc[i] = 0
        # 止盈
        if positions['stock'].iloc[i] > 0:
            current_value = positions['stock'].iloc[i] * data['close'].iloc[i]
            if (data['close'].iloc[i] / cost_basis - 1) > stop_profit:
                cash += positions['stock'].iloc[i] * data['close'].iloc[i]
                positions['stock'].iloc[i] = 0

        portfolio_value = cash + positions['stock'].iloc[i] * data['close'].iloc[i]
        portfolio_values.append(portfolio_value)

    portfolio = pd.DataFrame(index=data.index)
    portfolio['total'] = portfolio_values
    portfolio['returns'] = portfolio['total'].pct_change()
    return portfolio


def visualize(data, portfolio):
    if data.empty:
        print("数据为空，无法进行可视化。")
        return
    fig, axs = plt.subplots(3, 1, figsize=(14, 10))

    # 绘制K线图
    axs[0].plot(data['trade_date'], data['close'], label='Close Price')
    axs[0].plot(data[data['position'] == 1]['trade_date'], data[data['position'] == 1]['close'], '^', markersize=10,
                color='g', label='Buy Signal')
    axs[0].plot(data[data['position'] == -1]['trade_date'], data[data['position'] == -1]['close'], 'v', markersize=10,
                color='r', label='Sell Signal')
    axs[0].set_title('Stock Price with Trading Signals')
    axs[0].set_xlabel('Date')
    axs[0].set_ylabel('Price')
    axs[0].legend()

    # 绘制收益率曲线
    axs[1].plot(portfolio.index, portfolio['total'], label='Portfolio Value')
    axs[1].set_title('Portfolio Value over Time')
    axs[1].set_xlabel('Date')
    axs[1].set_ylabel('Value')
    axs[1].legend()

    # 绘制收益率柱状图
    axs[2].bar(portfolio.index, portfolio['returns'], label='Daily Returns')
    axs[2].set_title('Daily Returns')
    axs[2].set_xlabel('Date')
    axs[2].set_ylabel('Returns')
    axs[2].legend()

    plt.tight_layout()
    plt.show()


def compare_return_curves(all_portfolios, ts_codes):
    plt.figure(figsize=(14, 7))
    for i, portfolio in enumerate(all_portfolios):
        plt.plot(portfolio.index, portfolio['total'], label=ts_codes[i])
    plt.title('Comparison of Portfolio Value over Time')
    plt.xlabel('Date')
    plt.ylabel('Value')
    plt.legend()
    plt.show()


if __name__ == "__main__":
    ts_codes = ["920082.BJ", "600519.SH"]  # 可以添加更多股票代码
    start_date = "20230727"
    end_date = "20250221"
    all_data = fetch_stock_data(ts_codes, start_date, end_date)
    sorted_data = select_and_sort_stocks(all_data)
    all_portfolios = []
    for data in sorted_data:
        data = generate_trading_signals(data)
        portfolio = backtest(data)
        visualize(data, portfolio)
        all_portfolios.append(portfolio)

    if len(ts_codes) > 1:
        compare_return_curves(all_portfolios, ts_codes)

import tushare as ts
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 初始化pro接口
pro = ts.pro_api('276da5751ba9fda134012d591c37d0cbed93bdf1dad172617e265d6a')


def fetch_stock_data(ts_codes, start_date, end_date):
    all_data = []
    for ts_code in ts_codes:
        df = pro.daily(
            ts_code=ts_code,
            start_date=str(start_date),
            end_date=str(end_date),
            fields=[
                "ts_code",
                "trade_date",
                "open",
                "high",
                "low",
                "close",
                "pre_close",
                "change",
                "pct_chg",
                "vol",
                "amount"
            ]
        )
        df = df.sort_values(by='trade_date')
        df['trade_date'] = pd.to_datetime(df['trade_date'])
        all_data.append(df)
    return all_data


def generate_trading_signals(data):
    data['signal'] = 0
    short_window = 5
    long_window = 20
    data['short_mavg'] = data['close'].rolling(window=short_window, min_periods=1, center=False).mean()
    data['long_mavg'] = data['close'].rolling(window=long_window, min_periods=1, center=False).mean()
    data['signal'] = np.where(data['short_mavg'] > data['long_mavg'], 1, 0)
    data['position'] = data['signal'].diff()
    return data


def select_and_sort_stocks(all_data):
    sorted_data = sorted(all_data, key=lambda x: x['vol'].mean(), reverse=True)
    return sorted_data


def backtest(data, stop_loss=0.1, stop_profit=0.2):
    initial_capital = 100000.0
    positions = pd.DataFrame(index=data.index).fillna(0.0)
    # 确保 'stock' 列存在
    positions['stock'] = 0
    cash = initial_capital
    holdings = 0
    portfolio_values = []

    for i in range(len(data)):
        if data['position'].iloc[i] == 1:
            shares = cash // data['close'].iloc[i]
            positions['stock'].iloc[i] = shares
            cash -= shares * data['close'].iloc[i]
            cost_basis = data['close'].iloc[i]
        elif data['position'].iloc[i] == -1:
            cash += positions['stock'].iloc[i - 1] * data['close'].iloc[i]
            positions['stock'].iloc[i] = 0

        if positions['stock'].iloc[i] > 0:
            current_value = positions['stock'].iloc[i] * data['close'].iloc[i]
            if (data['close'].iloc[i] / cost_basis - 1) < -stop_loss:
                cash += positions['stock'].iloc[i] * data['close'].iloc[i]
                positions['stock'].iloc[i] = 0
            elif (data['close'].iloc[i] / cost_basis - 1) > stop_profit:
                cash += positions['stock'].iloc[i] * data['close'].iloc[i]
                positions['stock'].iloc[i] = 0

        portfolio_value = cash + positions['stock'].iloc[i] * data['close'].iloc[i]
        portfolio_values.append(portfolio_value)

    portfolio = pd.DataFrame(index=data.index)
    portfolio['total'] = portfolio_values
    portfolio['returns'] = portfolio['total'].pct_change()
    return portfolio


def visualize(data, portfolio):
    if data.empty:
        print("数据为空，无法进行可视化。")
        return
    fig, axs = plt.subplots(3, 1, figsize=(14, 10))

    axs[0].plot(data['trade_date'], data['close'], label='Close Price')
    axs[0].plot(data[data['position'] == 1]['trade_date'], data[data['position'] == 1]['close'], '^', markersize=10,
                color='g', label='Buy Signal')
    axs[0].plot(data[data['position'] == -1]['trade_date'], data[data['position'] == -1]['close'], 'v', markersize=10,
                color='r', label='Sell Signal')
    axs[0].set_title('Stock Price with Trading Signals')
    axs[0].set_xlabel('Date')
    axs[0].set_ylabel('Price')
    axs[0].legend()

    axs[1].plot(portfolio.index, portfolio['total'], label='Portfolio Value')
    axs[1].set_title('Portfolio Value over Time')
    axs[1].set_xlabel('Date')
    axs[1].set_ylabel('Value')
    axs[1].legend()

    axs[2].bar(portfolio.index, portfolio['returns'], label='Daily Returns')
    axs[2].set_title('Daily Returns')
    axs[2].set_xlabel('Date')
    axs[2].set_ylabel('Returns')
    axs[2].legend()

    plt.tight_layout()
    plt.show()


def compare_return_curves(all_portfolios, ts_codes):
    plt.figure(figsize=(14, 7))
    for i, portfolio in enumerate(all_portfolios):
        plt.plot(portfolio.index, portfolio['total'], label=ts_codes[i])
    plt.title('Comparison of Portfolio Value over Time')
    plt.xlabel('Date')
    plt.ylabel('Value')
    plt.legend()
    plt.show()


if __name__ == "__main__":
    ts_codes = ["920098.BJ", "920082.BJ"]
    start_date = 20230727
    end_date = 20250221
    all_data = fetch_stock_data(ts_codes, start_date, end_date)
    sorted_data = select_and_sort_stocks(all_data)
    all_portfolios = []
    for data in sorted_data:
        data = generate_trading_signals(data)
        portfolio = backtest(data)
        visualize(data, portfolio)
        all_portfolios.append(portfolio)

    if len(ts_codes) > 1:
        compare_return_curves(all_portfolios, ts_codes)

import tushare as ts
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 初始化pro接口
pro = ts.pro_api('276da5751ba9fda134012d591c37d0cbed93bdf1dad172617e265d6a')


def fetch_stock_data(ts_codes, start_date, end_date):
    all_data = []
    for ts_code in ts_codes:
        try:
            print(f"正在获取股票代码 {ts_code} 在 {start_date} 到 {end_date} 范围内的数据...")
            df = pro.daily(
                ts_code=ts_code,
                start_date=start_date,
                end_date=end_date,
                fields=[
                    "ts_code",
                    "trade_date",
                    "open",
                    "high",
                    "low",
                    "close",
                    "pre_close",
                    "change",
                    "pct_chg",
                    "vol",
                    "amount"
                ]
            )
            if df.empty:
                print(f"股票代码 {ts_code} 在 {start_date} 到 {end_date} 范围内没有数据。")
            else:
                print(f"成功获取股票代码 {ts_code} 的数据，数据行数: {len(df)}")
                df = df.sort_values(by='trade_date')
                df['trade_date'] = pd.to_datetime(df['trade_date'])
                all_data.append(df)
        except Exception as e:
            print(f"获取股票代码 {ts_code} 的数据时出现错误: {e}")
    return all_data


def generate_trading_signals(data):
    data['signal'] = 0
    short_window = 5
    long_window = 20
    data['short_mavg'] = data['close'].rolling(window=short_window, min_periods=1, center=False).mean()
    data['long_mavg'] = data['close'].rolling(window=long_window, min_periods=1, center=False).mean()
    data['signal'] = np.where(data['short_mavg'] > data['long_mavg'], 1, 0)
    data['position'] = data['signal'].diff()
    return data


def backtest(data):
    initial_capital = float(100000.0)
    positions = pd.DataFrame(index=data.index).fillna(0.0)
    if not data.empty:
        positions['stock'] = data['signal'] * initial_capital / data['close'][0]
    else:
        positions['stock'] = 0
    portfolio = positions.multiply(data['close'], axis=0)
    pos_diff = positions.diff()
    portfolio['holdings'] = (positions.multiply(data['close'], axis=0)).sum(axis=1)
    portfolio['cash'] = initial_capital - (pos_diff.multiply(data['close'], axis=0)).sum(axis=1).cumsum()
    portfolio['total'] = portfolio['cash'] + portfolio['holdings']
    portfolio['returns'] = portfolio['total'].pct_change()
    return portfolio


def visualize(data, portfolio):
    if data.empty:
        print("数据为空，无法进行可视化。")
        return
    fig, axs = plt.subplots(3, 1, figsize=(14, 10))

    # 绘制K线图
    axs[0].plot(data['trade_date'], data['close'], label='Close Price')
    axs[0].plot(data[data['position'] == 1]['trade_date'], data[data['position'] == 1]['close'], '^', markersize=10,
                color='g', label='Buy Signal')
    axs[0].plot(data[data['position'] == -1]['trade_date'], data[data['position'] == -1]['close'], 'v', markersize=10,
                color='r', label='Sell Signal')
    axs[0].set_title('Stock Price with Trading Signals')
    axs[0].set_xlabel('Date')
    axs[0].set_ylabel('Price')
    axs[0].legend()

    # 绘制收益率曲线
    axs[1].plot(portfolio.index, portfolio['total'], label='Portfolio Value')
    axs[1].set_title('Portfolio Value over Time')
    axs[1].set_xlabel('Date')
    axs[1].set_ylabel('Value')
    axs[1].legend()

    # 绘制收益率柱状图
    axs[2].bar(portfolio.index, portfolio['returns'], label='Daily Returns')
    axs[2].set_title('Daily Returns')
    axs[2].set_xlabel('Date')
    axs[2].set_ylabel('Returns')
    axs[2].legend()

    plt.tight_layout()
    plt.show()


if __name__ == "__main__":
    # 更换为常见的 A 股代码
    ts_codes = ["600519.SH"]
    start_date = "20230101"
    end_date = "20240101"
    all_data = fetch_stock_data(ts_codes, start_date, end_date)
    for data in all_data:
        data = generate_trading_signals(data)
        portfolio = backtest(data)
        visualize(data, portfolio)
import tushare as ts
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 初始化pro接口
pro = ts.pro_api('276da5751ba9fda134012d591c37d0cbed93bdf1dad172617e265d6a')


def fetch_stock_data(ts_codes, start_date, end_date):
    all_data = []
    for ts_code in ts_codes:
        try:
            print(f"正在获取股票代码 {ts_code} 在 {start_date} 到 {end_date} 范围内的数据...")
            df = pro.daily(
                ts_code=ts_code,
                start_date=start_date,
                end_date=end_date,
                fields=[
                    "ts_code",
                    "trade_date",
                    "open",
                    "high",
                    "low",
                    "close",
                    "pre_close",
                    "change",
                    "pct_chg",
                    "vol",
                    "amount"
                ]
            )
            if df.empty:
                print(f"股票代码 {ts_code} 在 {start_date} 到 {end_date} 范围内没有数据。")
            else:
                print(f"成功获取股票代码 {ts_code} 的数据，数据行数: {len(df)}")
                df = df.sort_values(by='trade_date')
                df['trade_date'] = pd.to_datetime(df['trade_date'])
                all_data.append(df)
        except Exception as e:
            print(f"获取股票代码 {ts_code} 的数据时出现错误: {e}")
    return all_data


def generate_trading_signals(data):
    data['signal'] = 0
    short_window = 5
    long_window = 20
    data['short_mavg'] = data['close'].rolling(window=short_window, min_periods=1, center=False).mean()
    data['long_mavg'] = data['close'].rolling(window=long_window, min_periods=1, center=False).mean()
    data['signal'] = np.where(data['short_mavg'] > data['long_mavg'], 1, 0)
    data['position'] = data['signal'].diff()
    return data


def backtest(data):
    initial_capital = float(100000.0)
    positions = pd.DataFrame(index=data.index).fillna(0.0)
    if not data.empty:
        positions['stock'] = data['signal'] * initial_capital / data['close'][0]
    else:
        positions['stock'] = 0
    portfolio = positions.multiply(data['close'], axis=0)
    pos_diff = positions.diff()
    portfolio['holdings'] = (positions.multiply(data['close'], axis=0)).sum(axis=1)
    portfolio['cash'] = initial_capital - (pos_diff.multiply(data['close'], axis=0)).sum(axis=1).cumsum()
    portfolio['total'] = portfolio['cash'] + portfolio['holdings']
    portfolio['returns'] = portfolio['total'].pct_change()
    return portfolio


def visualize(data, portfolio):
    if data.empty:
        print("数据为空，无法进行可视化。")
        return
    fig, axs = plt.subplots(3, 1, figsize=(14, 10))

    # 绘制K线图
    axs[0].plot(data['trade_date'], data['close'], label='Close Price')
    axs[0].plot(data[data['position'] == 1]['trade_date'], data[data['position'] == 1]['close'], '^', markersize=10,
                color='g', label='Buy Signal')
    axs[0].plot(data[data['position'] == -1]['trade_date'], data[data['position'] == -1]['close'], 'v', markersize=10,
                color='r', label='Sell Signal')
    axs[0].set_title('Stock Price with Trading Signals')
    axs[0].set_xlabel('Date')
    axs[0].set_ylabel('Price')
    axs[0].legend()

    # 绘制收益率曲线
    axs[1].plot(portfolio.index, portfolio['total'], label='Portfolio Value')
    axs[1].set_title('Portfolio Value over Time')
    axs[1].set_xlabel('Date')
    axs[1].set_ylabel('Value')
    axs[1].legend()

    # 绘制收益率柱状图
    axs[2].bar(portfolio.index, portfolio['returns'], label='Daily Returns')
    axs[2].set_title('Daily Returns')
    axs[2].set_xlabel('Date')
    axs[2].set_ylabel('Returns')
    axs[2].legend()

    plt.tight_layout()
    plt.show()


def compare_return_curves(all_portfolios, ts_codes):
    plt.figure(figsize=(14, 7))
    for i, portfolio in enumerate(all_portfolios):
        plt.plot(portfolio.index, portfolio['total'], label=ts_codes[i])
    plt.title('Comparison of Portfolio Value over Time')
    plt.xlabel('Date')
    plt.ylabel('Value')
    plt.legend()
    plt.show()


if __name__ == "__main__":
    ts_codes = ["920099.BJ", "920116.BJ", "920098.BJ", "920082.BJ"]
    start_date = "20230727"
    end_date = "20250221"
    all_data = fetch_stock_data(ts_codes, start_date, end_date)
    all_portfolios = []
    for data in all_data:
        data = generate_trading_signals(data)
        portfolio = backtest(data)
        visualize(data, portfolio)
        all_portfolios.append(portfolio)

    if len(ts_codes) > 1:
        compare_return_curves(all_portfolios, ts_codes)

import tushare as ts
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.dates as mdates

# 初始化pro接口
pro = ts.pro_api('276da5751ba9fda134012d591c37d0cbed93bdf1dad172617e265d6a')


# 数据获取函数
def fetch_stock_data(ts_codes, start_date, end_date):
    all_data = []
    for ts_code in ts_codes:
        try:
            print(f"正在获取股票代码 {ts_code} 在 {start_date} 到 {end_date} 范围内的数据...")
            df = pro.daily(ts_code=ts_code, start_date=start_date, end_date=end_date, fields=[
                "ts_code", "trade_date", "open", "high", "low", "close", "pre_close", "change", "pct_chg", "vol",
                "amount"
            ])
            if df.empty:
                print(f"股票代码 {ts_code} 在 {start_date} 到 {end_date} 范围内没有数据。")
                continue
            df['trade_date'] = pd.to_datetime(df['trade_date'])
            df = df.sort_values(by='trade_date')
            all_data.append(df)
            print(f"成功获取股票代码 {ts_code} 的数据，数据行数: {len(df)}")
        except Exception as e:
            print(f"获取股票代码 {ts_code} 的数据时出现错误: {e}")
    return all_data


# 生成交易信号
def generate_trading_signals(data, short_window=5, long_window=20):
    data['short_mavg'] = data['close'].rolling(window=short_window, min_periods=1).mean()
    data['long_mavg'] = data['close'].rolling(window=long_window, min_periods=1).mean()
    data['signal'] = np.where(data['short_mavg'] > data['long_mavg'], 1, 0)
    data['position'] = data['signal'].diff()
    return data


# 回测函数
def backtest(data, initial_capital=100000.0):
    data['position'] = data['position'].fillna(0)
    data['holdings'] = data['close'] * data['signal'].shift(1).fillna(0)
    data['cash'] = initial_capital - data['holdings'].cumsum()
    data['total'] = data['cash'] + data['holdings']
    data['returns'] = data['total'].pct_change()
    return data


# 可视化函数
def visualize(data):
    fig, ax = plt.subplots(3, 1, figsize=(14, 10))

    # K线图
    ax[0].plot(data['trade_date'], data['close'], label='Close Price')
    ax[0].plot(data[data['position'] == 1]['trade_date'], data[data['position'] == 1]['close'], '^', markersize=10,
               color='g', label='Buy Signal')
    ax[0].plot(data[data['position'] == -1]['trade_date'], data[data['position'] == -1]['close'], 'v', markersize=10,
               color='r', label='Sell Signal')
    ax[0].set_title('Stock Price with Trading Signals')
    ax[0].set_xlabel('Date')
    ax[0].set_ylabel('Price')
    ax[0].legend()
    ax[0].xaxis.set_major_locator(mdates.MonthLocator())
    ax[0].xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))

    # 收益率曲线
    ax[1].plot(data['trade_date'], data['total'], label='Portfolio Value')
    ax[1].set_title('Portfolio Value over Time')
    ax[1].set_xlabel('Date')
    ax[1].set_ylabel('Value')
    ax[1].legend()
    ax[1].xaxis.set_major_locator(mdates.MonthLocator())
    ax[1].xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))

    # 日收益率
    ax[2].bar(data['trade_date'], data['returns'], label='Daily Returns')
    ax[2].set_title('Daily Returns')
    ax[2].set_xlabel('Date')
    ax[2].set_ylabel('Returns')
    ax[2].legend()
    ax[2].xaxis.set_major_locator(mdates.MonthLocator())
    ax[2].xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))

    plt.tight_layout()
    plt.show()


# 比较收益率曲线
def compare_return_curves(portfolios, ts_codes):
    plt.figure(figsize=(14, 7))
    for i, portfolio in enumerate(portfolios):
        plt.plot(portfolio['trade_date'], portfolio['total'], label=ts_codes[i])
    plt.title('Comparison of Portfolio Value over Time')
    plt.xlabel('Date')
    plt.ylabel('Value')
    plt.legend()
    plt.gca().xaxis.set_major_locator(mdates.MonthLocator())
    plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
    plt.show()


# 主程序
if __name__ == "__main__":
    ts_codes = ["920099.BJ", "920116.BJ", "920098.BJ", "920082.BJ"]
    start_date = "20230727"
    end_date = "20250221"

    all_data = fetch_stock_data(ts_codes, start_date, end_date)
    portfolios = []

    for data in all_data:
        data = generate_trading_signals(data)
        portfolio = backtest(data)
        visualize(data)
        portfolios.append(portfolio)

    if len(portfolios) > 1:
        compare_return_curves(portfolios, ts_codes)